(function(){
var MainData = {
    api_url: 'http://xn.hf.fminutes.com/api.php?',
    ini_url: 'http://xnimg.cn/xcube/app/23163/xn35/module/ini.xml'
};

var SecurityKey = {
    get encodeKey(){
        return 'inu';
    }
};

var HappyFarm = {
    _failed: true,
    _timer: 0,
    _timeShift: 0,
    _url: 'http://apps.xiaonei.com/happyfarm',
    startup: function(){
        this.conn = new ConnectionProxy({preRequest:preRequest, preHandler:preHandler, url: MainData.api_url});

        var uri = IOProxy.newURI(HappyFarm._url, null, null);
        gBrowser.addEventListener('load', function(){
            if(gBrowser.currentURI.equals(uri)) {
                if(HappyFarm._failed) { //部分数据更新失败且不是正在更新时尝试刷新失败部分的数据
                    ConsoleProxy.log('尝试刷新...');
                    HappyFarm.refreshInfo();
                }
            }
        }, false);
        this.resume();
        return;

        function preRequest(url){
            var farmTime = HappyFarm.getServerTime();
            var farmKey = hex_md5(farmTime+SecurityKey.encodeKey);
            return url +'&farmKey='+farmKey+'&farmTime='+farmTime;
        }

        function preHandler(req, conf, handler, error){
            var output = req.responseText;
            if(req.status != '200') return callError({}, req);
            if(output.length) {
                try{
                    output = output.replace(/<!--.*-->$/, '');
                    var json = eval('('+output+')');
                    if(json.error) {
                        callError(json, req);
                    }
                    else handler(json, output);
                } catch(e){
                    ConsoleProxy.reportError(e);
                }
            } else {
                callError({}, req);
            }

            return;

            function callError(err, req){
                if(error) error(err, req);
                else HappyFarm.onError(err, req);
            }
        }
    },
    pause: function(){
        clearInterval(this._timer);
        this._timer = 0;
        this.tooltip();
    },
    resume: function(){
        if(this._timer) return;
        this.refreshInfo();
        this._timer = window.setInterval(function(){
            HappyFarm.refreshInfo();
        }, 300000);
        this.tooltip();
    },
    tooltip: function(msg){
        this.setTooltip('左键: 打开开心农场\n中键: 切换到"'+(this._timer?'手动':'自动')+'"状态\n右键: 杀虫、浇水、除草、收获一条龙'+(msg?'\n'+msg:''));
    },
    toggle: function(){
        if(this._timer) {
            this.pause();
            alert('HappyFarm Toy: 已切换到"手动"状态!');
        } else {
            this.resume();
            alert('HappyFarm Toy: 已切换到"自动"状态!');
        }
    },
    click: function(e){
        if(e.button === 0) {//Left button
            this._openTab();
        } else if(e.button === 1) {//Middle button
            this.toggle();
            this._updateInfo();
        } else if(e.button === 2) {//Right button
            this.refreshInfo();
        } else {
            //What happend? @@
        }
    },
    refreshInfo: function(){
        var updateChain = new CallChain();
        if(!this._crops) updateChain.append(this.getCropsInfo, this);
        updateChain.append(this.run, this).callAll(this._refreshInfo);
    },
    _refreshInfo: function(){
        var clearWeedChain = new CallChain();
        var spayingChain = new CallChain();
        var waterChain = new CallChain();
        var harvestChain = new CallChain();
        var changed = false;

        for(var place in HappyFarm._userInfo.farmlandStatus){
            var field = HappyFarm._userInfo.farmlandStatus[place];
            for(var weed = 0; weed < parseInt(field.f); weed++) //除草
                clearWeedChain.append(HappyFarm.clearWeed, HappyFarm, [HappyFarm._userInfo.user.uId, place]);
            for(var pest = 0; pest < parseInt(field.g); pest++) //杀虫
                spayingChain.append(HappyFarm.spraying, HappyFarm, [HappyFarm._userInfo.user.uId, place]);
            if(!parseInt(field.h)) waterChain.append(HappyFarm.water, HappyFarm, [HappyFarm._userInfo.user.uId, place]);    //浇水
            if(parseInt(field.k)) harvestChain.append(HappyFarm.harvest, HappyFarm, [HappyFarm._userInfo.user.uId, place]); //收获
            changed = changed || parseInt(field.f) || parseInt(field.g) || !parseInt(field.h) || parseInt(field.k);
        };

        var cc = new CallChain()
            .append(clearWeedChain.callAll, clearWeedChain)
            .append(spayingChain.callAll, spayingChain)
            .append(waterChain.callAll, waterChain)
            .append(harvestChain.callAll, harvestChain);
        if(!HappyFarm._suggest) cc.append(HappyFarm.getSeedInfo, HappyFarm);
        cc.append(HappyFarm.getFriendList, HappyFarm);
        if(changed) cc.append(HappyFarm.run, HappyFarm);
        cc.append(HappyFarm.nice, HappyFarm)
            .callAll(function(){
                HappyFarm._updateInfo();
            });
    },
    nice: function(handler){
        this._nice = {f:{}, g:{}, h:{}, k:{}};
        var Run2Chain = new CallChain();
        if(this._friendList){
            for each(var f in this._friendList){
                if(f.userId != HappyFarm._userInfo.user.uId)    //跳过TK自己...
                    Run2Chain.append(HappyFarm.run2, HappyFarm, [f.userId]);
            }
        }
        Run2Chain.callAll(handler);
    },
    getCropsInfo: function(handler){
        var conn = new ConnectionProxy();
        this.setStatus('种子...');
        conn.get(MainData.ini_url, null, function(req){
            try{
                //var doc = new DOMParser().parseFromString(req.responseText, 'text/xml');
                var doc = req.responseXML;
                var crops = XPathProxy.evaluateXPath(doc, '//crops/crop/@id');
                var cropGrow = XPathProxy.evaluateXPath(doc, '//crops/crop/cropGrow/@value');
                var cId = [node.value for each(node in crops)];
                var grow = [node.value.split(',') for each(node in cropGrow)];
                var _crops = {};
                for(var i =0; i<cId.length; i++){
                    _crops[cId[i]] = grow[i];
                }
                HappyFarm._crops = _crops;
                HappyFarm.setStatus('种子...完成!');
                if(handler) handler();
            }catch(e){
                HappyFarm.setStatus('种子...失败!');
                HappyFarm._dumpRequest({error:'解析种子信息失败'}, req);
                if(handler) handler();
            }
        }, function(err, req){
            HappyFarm.setStatus('种子...失败!');
            HappyFarm._dumpRequest(err, req);
            if(handler) handler();
        });
    },
    _updateInfo: function(){
        var userInfo = this._userInfo;
        if(!userInfo) return;
        var lands = userInfo.farmlandStatus;
        var harvest = lands.reduce(function(prev, curr){return parseInt(curr.k)?prev+1:prev;}, 0);
        var weed = lands.some(function(elem){return parseInt(elem.f);}); //Test weed
        var pest = lands.some(function(elem){return parseInt(elem.g);}); //Test pest
        var water = lands.some(function(elem){return parseInt(elem.h);}); //Test water
        var statusArray = [];
        if(userInfo.dog && parseInt(userInfo.dog.dogId) && parseInt(userInfo.dog.isHungry)) statusArray.push('小黑饿了!');
        if(harvest) statusArray.push(harvest+'已成熟!');
        if(weed) statusArray.push('杂草!');
        if(pest) statusArray.push('虫子!');
        if(!water) statusArray.push('浇水!');
        var level = Math.floor(this.level(parseInt(userInfo.exp)));
        statusArray.push('等级:'+level);
        statusArray.push('升级经验:'+(this.exp(level+1)-parseInt(userInfo.exp)));
        statusArray.push('排名:'+this.rank(userInfo.user.uId));
        statusArray.push('金钱:'+userInfo.user.money);
        this.setStatus(statusArray.join('|'));

        var tipArray = [];
        tipArray.push('');
        var goodsIds = {};
        var count = 0;
        var goodsArray = [];
        if(HappyFarm._friendList){
            ['f', 'g', 'h'].forEach(function(type){
                for(var uId in HappyFarm._nice[type]){
                    if(HappyFarm._nice[type][uId] && !goodsIds[uId]) {
                        goodsArray.push(HappyFarm._friendList[uId].userName);
                        goodsIds[uId] = true;
                        count++;
                    }
                }
            });
        }
        tipArray.push('~做好事去: '+(count?goodsArray.join(','):'没啥好事可做'));

        count = 0;
        var badsArray = [];
        if(HappyFarm._friendList){
            for(var uId in HappyFarm._nice['k']){
                if(HappyFarm._nice['k'][uId]) {
                    badsArray.push(HappyFarm._friendList[uId].userName);
                    count++;
                }
            }
        }
        tipArray.push('~做坏事去: '+(count?badsArray.join(','):'没啥坏事可做'));
        if(this._suggest) {
            tipArray.push('');
            tipArray.push(this._suggest);
        }
        this.tooltip(tipArray.join('\n'));
        this._failed = !(this._crops && this._friendList);
    },
    level: function(exp){
        return Math.sqrt((exp+25)/100)-0.5;
    },
    exp: function(level){
        return level*(level+1)*100;
    },
    rank: function(uId){
        if(this._friendList){
            return (this._friendList[uId].rank+1)||'(未知)';
        } else {
            return '(未知)';
        }
    },
    setStatus: function(status){
        var panel = document.getElementById('hfpanel');
        panel.label = status;
    },
    setTooltip: function(text){
        var panel = document.getElementById('hfpanel');
        panel.tooltipText = text;
    },
    run: function(handler){
        this.conn.post('mod=user&act=run&new=1', null, function(userInfo, output){
            ConsoleProxy.info('<<运行>>'+output);
            HappyFarm._userInfo = userInfo;
            HappyFarm._timeShift = Math.floor(Date.now()/1000-parseInt(userInfo.serverTime.time));
            if(handler) handler(userInfo);
        }, function(err, req){
            HappyFarm.onError(err, req);
        }, 3);
    },
    run2: function(ownerId, handler){
        HappyFarm.setStatus('tk'+HappyFarm._friendList[ownerId].userName+'...');
        this.conn.post('mod=user&act=run&flag=1&new=1', {ownerId: ownerId}, function(data, output){
            ConsoleProxy.info('<<TK>>'+HappyFarm._friendList[ownerId].userName+':'+output);
            for(var place in data.farmlandStatus){
                var field = data.farmlandStatus[place];
                if(parseInt(field.b)>=7) continue;      //已经收过
                if(parseInt(field.f)) HappyFarm._nice.f[ownerId] = true;
                if(parseInt(field.g)) HappyFarm._nice.g[ownerId] = true;
                if(!parseInt(field.h)) HappyFarm._nice.h[ownerId] = true;
                if(parseInt(field.k) && parseInt(field.m)>parseInt(field.l) && (!data.dog || !parseInt(data.dog.dogId) || parseInt(data.dog.isHungry))) {
                    var stolen = false;
                    for(var uId in field.n){
                        if(uId == HappyFarm._userInfo.user.uId) {
                            stolen = true;
                            break;
                        }
                    }
                    if(!stolen) HappyFarm._nice.k[ownerId] = true;
                }
            }
            HappyFarm.setStatus('tk'+HappyFarm._friendList[ownerId].userName+'...成功!');
            if(handler) handler(data);
        }, function(err, req){
            HappyFarm.setStatus('tk'+HappyFarm._friendList[ownerId].userName+'...跳过!');
            ConsoleProxy.log('<<跳过TK>>'+HappyFarm._friendList[ownerId].userName);
            HappyFarm._dumpRequest(err, req);
            if(handler) handler();
        });
    },
    water: function(ownerId, place, handler){
        this.setStatus('浇水['+place+']...');
        this.conn.post('mod=farmlandstatus&act=water', {ownerId: ownerId, place: place}, function(data){
            ConsoleProxy.info('<<浇水>>OwnerID: '+ownerId+'\t'+data.direction);
            HappyFarm.setStatus('浇水['+place+']...完成!');
            if(handler) handler(data);
        }, function(err, req){
            HappyFarm.setStatus('浇水['+place+']...失败!');
            HappyFarm._dumpRequest(err, req);
            if(handler) handler();
        });
    },
    clearWeed: function(ownerId, place, handler){
        HappyFarm.setStatus('除草['+place+']...');
        this.conn.post('mod=farmlandstatus&act=clearWeed', {ownerId: ownerId, place: place}, function(data){
            ConsoleProxy.info('<<除草>>OwnerID: '+ownerId+'\t'+data.direction);
            HappyFarm.setStatus('除草['+place+']...完成!');
            if(handler) handler(data);
        }, function(err, req){
            HappyFarm.setStatus('除草['+place+']...失败!');
            HappyFarm._dumpRequest(err, req);
            if(handler) handler();
        });
    },
    spraying: function(ownerId, place, handler){
        HappyFarm.setStatus('杀虫['+place+']...');
        this.conn.post('mod=farmlandstatus&act=spraying', {ownerId: ownerId, place: place}, function(data){
            ConsoleProxy.info('<<杀虫>>OwnerID: '+ownerId+'\t'+data.direction);
            if(handler) handler(data);
            HappyFarm.setStatus('杀虫['+place+']...成功!');
        }, function(err, req){
            HappyFarm.setStatus('杀虫['+place+']...失败!');
            HappyFarm._dumpRequest(err, req);
            if(handler) handler();
        });
    },
    harvest: function(ownerId, place, handler){
        HappyFarm.setStatus('收获['+place+']...');
        this.conn.post('mod=farmlandstatus&act=harvest', {ownerId: ownerId, place: place}, function(data){
            ConsoleProxy.info('<<收获>>OwnerID: '+ownerId+'\t'+data.direction);
            HappyFarm.setStatus('收获['+place+']...成功!');
            if(handler) handler(data);
        }, function(err, req){
            HappyFarm.setStatus('收获['+place+']...失败!');
            HappyFarm._dumpRequest(err, req);
            if(handler) handler();
        }, 3);
    },
    getFriendList: function(handler){
        this.setStatus('好友列表...');
        this.conn.post('mod=friend', {refresh: true}, function(data, output){
            if(data.length||!HappyFarm._friendList) {
                var fl = {};
                var data = data.sort(function(a, b){
                    return -(a.exp-b.exp);
                });
                for(var i in data){
                    var d = data[i];
                    d.rank = parseInt(i);
                    fl[data[i].userId] = d;
                }
                HappyFarm._friendList = fl;
                HappyFarm._fl_time = Date.now();
                ConsoleProxy.info('<<好友列表>>'+output);
            }
            HappyFarm.setStatus('好友列表...完成!');
            if(handler) handler(data);
        }, function(err, req){
            HappyFarm.setStatus('好友列表...失败!');
            HappyFarm._dumpRequest(err, req);
            if(handler) handler();
        }, 3);
    },
    getSeedInfo: function(handler){
        function trueLength(str){
            var l = 0;
            for(var i=0; i<str.length; i++){
                l+=(str.charCodeAt(i)>255?2:1);
            }
            return l;
        }

        function pendSpace(str, length){
            var space = '';
            for(var i=0; i<length-trueLength(str); i++){
                space +=' ';
            }
            return str+space;
        }
        if(this._crops){
            HappyFarm.setStatus('生成参考...');
            var crops = this._crops;
            var level = Math.floor(this.level(parseInt(this._userInfo.exp)));
            this.conn.post('mod=repertory&act=getSeedInfo', {}, function(data, output){
                var unit = data.map(function(seed){
                    var totalTime = (parseInt(seed.growthCycle)+(parseInt(seed.maturingTime)-1)*(parseInt(seed.growthCycle)-parseInt(crops[seed.cId][2])))/3600;
                    return [(parseInt(seed.expect)-parseInt(seed.price))/totalTime, (parseInt(seed.cropExp)*parseInt(seed.maturingTime))/totalTime];
                });
                var valuable = [i for(i in data)];
                var expable = [i for(i in data)];
                valuable.sort(function(c1, c2){
                    var l1 = parseInt(data[c1].cLevel);
                    var l2 = parseInt(data[c2].cLevel);
                    var u1 = unit[c1][0];
                    var u2 = unit[c2][0];
                    if(l1>level) u1 -= 999;
                    if(l2>level) u2 -= 999;
                    return -(u1-u2);
                });
                expable.sort(function(c1, c2){
                    var l1 = parseInt(data[c1].cLevel);
                    var l2 = parseInt(data[c2].cLevel);
                    var u1 = unit[c1][1];
                    var u2 = unit[c2][1];
                    if(l1>level) u1 -= 999;
                    if(l2>level) u2 -= 999;
                    return -(u1-u2);
                });
                var idx = [];
                var left = [data[v].cName+': '+unit[v][0].toFixed(2) for each(v in valuable) if(parseInt(data[v].cLevel)<=level)];
                var right = [data[e].cName+': '+unit[e][1].toFixed(2) for each(e in expable) if(parseInt(data[e].cLevel)<=level)];
                var title = ['', '  价值参考  ', '  经验参考  '];
                var max = title.map(function(e) trueLength(e));
                for(var i=0; i<left.length; i++){
                    idx[i] = (i+1).toString();
                    max[0] = Math.max(trueLength(idx[i]), max[0]);
                    max[1] = Math.max(trueLength(left[i]), max[1]);
                    max[2] = Math.max(trueLength(right[i]), max[2]);
                }
                var suggest = ['|'+pendSpace(idx[i], max[0])+'|'+pendSpace(left[i], max[1])+'|'+pendSpace(right[i], max[2])+'|' for(i in idx)].join('\n');
                HappyFarm._suggest = '|'+pendSpace(title[0], max[0])+'|'+pendSpace(title[1], max[1])+'|'+pendSpace(title[2], max[2])+'|\n'+suggest;
                HappyFarm.setStatus('价值参考...成功!');
                if(handler) handler(data);
            }, function(err, req){
                HappyFarm.setStatus('价值参考...失败!');
                HappyFarm._dumpRequest(err, req);
                if(handler) handler();
            });

        } else {
            ConsoleProxy.log('无种子列表，不可计算价值参考!');
            if(handler) handler();
        }
    },
    getServerTime: function(){
        return Math.floor(Date.now()/1000-this._timeShift);
    },
    onError: function(err, req){
        this._failed = true;
        this.setStatus(err.error?err.error:'左键打开开心农场!');
        HappyFarm._dumpRequest(err, req);
    },
    _dumpRequest: function(data, req){
        try{
            ConsoleProxy.log(
                (data.error?'开心农场: '+data.error+'\n':'')
                +'URL: '+req.channel.URI.spec
                +'\nDUMP Response:\n'+req.status+' '+req.statusText+'\n'
                +req.getAllResponseHeaders()+'\n'
                +req.responseText
            );
        }catch(e){
            ConsoleProxy.log(
                (data.error?'开心农场: '+data.error+'\n':'')
                +'URL: '+req.channel.URI.spec
            );
        }
    },
    _openTab: function(){
        gBrowser.selectedTab = gBrowser.addTab(this._url);
    }
};

function ConnectionProxy(conf){
    this._conf = conf||{};
}

ConnectionProxy.prototype = {
    post: function(url, params, handler, error, retry){
        return this.request('POST', url, params, handler, error, retry);
    },
    get: function(url, params, handler, error, retry){
        return this.request('GET', url, params, handler, error, retry);
    },
    request: function(method, url, params, handler, error, retry){
        var conf = this._conf;
        if(/^http:/.test(url)) var fullUrl = url;
        else var fullUrl = conf.url+url;

        if(conf.preRequest){
            fullUrl = this._conf.preRequest(fullUrl);
        }

        var req = new XMLHttpRequest();
        req.open(method.toUpperCase(), fullUrl, conf.async===false?false:true);
        var paramString = null;
        if(method.toLowerCase() === 'post'){
            if(typeof params === 'string') {
                var paramString = params;
            } else if(params){
                var paramArray = new Array();
                for (var p in params){
                    paramArray.push(p+'='+encodeURIComponent(params[p]));
                }
                paramString = paramArray.join('&');
            } else {
                paramString = '';
            }
            req.setRequestHeader('Content-Length', paramString.length);
            req.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
        }
        var ok = false;
        req.onload = function(){
            ok = true;
            if(conf.preHandler){
                conf.preHandler(req, conf, handler, error);
            } else if(req.status != 200) {
                handleError(err, req);
            } else {
                if(handler) handler(req);
            }
        };
        var timer = window.setTimeout(function(){
            if(!ok){
                req.abort();
                handleError({error: '请求超时!'}, req);
            }
        }, 5000);
        req.send(paramString);
        var _self = this;
        return req;

        function handleError(err, req){
            if(retry && retry>0){
                _self.request(method, url, params, handler, error, retry-1);
            } else {
                if(error) error(err, req);
            }
        }
    }
};

var CallChain = function(){
    this._chain = [];
};

CallChain.prototype = {
    append: function(func, obj, params){
        var paramsClone = [p for each(p in params)];
        this._chain.push([func, obj, paramsClone]);
        return this;
    },
    callAll: function(done, handler){
        this._done = done;
        this._handler = handler;
        this.next();
    },
    next: function(){
        var item = this._chain.shift();
        if(!item) {
            if(this._done) this._done();
            if(this._handler) this._handler();
            return;
        }

        var func = item[0];
        var obj = item[1];
        var params = item[2]||[];
        var _self = this;
        params.push(function(){_self.next();});
        func.apply(obj, params);
    }
};

var ConsoleProxy = {
    info: function(msg){
//        this.log(msg);    //for debug
    },
    log: function(msg){
        try{
            var aConsoleService = Components.classes["@mozilla.org/consoleservice;1"].
                getService(Components.interfaces.nsIConsoleService);

            aConsoleService.logStringMessage(msg);
        }catch(e){} //ignore all log error
    },
    reportError: function(err){
        Components.utils.reportError(err);
    }
};

var IOProxy = {
    newURI: function(aURL, aOriginCharset, aBaseURI){
        var ios = Components.classes["@mozilla.org/network/io-service;1"].getService(Components.interfaces.nsIIOService);
        return ios.newURI(aURL, aOriginCharset, aBaseURI);
    }
};

var XPathProxy = {
    // Evaluate an XPath expression aExpression against a given DOM node
    // or Document object (aNode), returning the results as an array
    // thanks wanderingstan at morethanwarm dot mail dot com for the
    // initial work.
    evaluateXPath: function(aNode, aExpr) {
      var xpe = new XPathEvaluator();
      var nsResolver = xpe.createNSResolver(aNode.ownerDocument == null ?
        aNode.documentElement : aNode.ownerDocument.documentElement);
      var result = xpe.evaluate(aExpr, aNode, nsResolver, 0, null);
      var found = [];
      var res;
      while (res = result.iterateNext())
        found.push(res);
      return found;
    }
        
};

window.HappyFarm = HappyFarm;

window.addEventListener('load', function(){HappyFarm.startup();}, false);

})();
